package be.ipl.finito.domaine;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;

import be.ipl.finito.util.Util;

@SuppressWarnings("serial")
@Entity
@Table(name = "PLATEAUX", schema = "FINITO")
public class Plateau implements Serializable {

	public static final int nbrCases = 36;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;

	@ManyToOne
	@JoinColumn(name = "JOUEUR_ID")
	private Joueur joueur;

	@ManyToOne
	@JoinColumn(name = "PARTIE_ID")
	private Partie partie;

	@Transient
	private int score;

	@Transient
	private int nombreDeCasePlace = 0;

	@OneToMany(cascade = { CascadeType.ALL })
	@JoinColumn(name = "PLATEAUX_ID")
	private List<Case> lesCases = new ArrayList<Case>(12);

	@Transient
	private List<Jeton> jetonsRetournes = new ArrayList<Jeton>(3);

	protected Plateau() {

	}

	public Plateau(Joueur joueur, Partie partie) {
		super();
		Util.checkObject(joueur);
		Util.checkObject(partie);
		this.joueur = joueur;
		this.partie = partie;
		score = 0;
		for (int i = 0; i < this.lesCases.size(); i++) {
			this.lesCases.add(new Case(i+1));
		}
	}

	public int getId() {
		return id;
	}

	public Joueur getJoueur() {
		return joueur;
	}

	public Partie getPartie() {
		return partie;
	}

	public int getScore() {
		return score;
	}

	public List<Case> getLesCases() {
		return lesCases;
	}

	public List<Jeton> getJetonsRetournes() {
		return jetonsRetournes;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;  
		return result;
	}

	// Si place rempli gerer par l'IHM il ne laissera pas appeler de methode si
	// la case est utilisé
	// p-e false si la table est pleine
	public boolean ajouterJeton(Case case1) {
		Util.checkObject(case1);	
		return lesCases.add(case1);
	}
	
	
	//par exemple si le dé affiche 8 
	//map position  8 -> XX
	//faire appel a la map pour trouver la correspondance
//	public ajouterALaCase(Jeton jeton, int numero){
//		Map <Integer, Integer> MapCasePositionsPourDe = MapCasePositionsPourDeInit();
//		List<Integer> listePositionsAutorisees = new ArrayList<Integer>();
//		
//		listePositionsAutorisees = isAutorised(MapCasePositionsPourDe.get(partie.getDe()));
//		
//		if(listePositionsAutorisees.contains(numero)){
//			this.positionPlace = numero;
//			return new Case
//		}
//	}
	

	public boolean SupprimerJeton(Case case1){
		Util.checkObject(case1);
		return lesCases.remove(case1);
	}
	

	
	
	public Map<Integer, Integer> MapCasePositionsPourDeInit(){
		int positionDansTableau = 0;
		Map<Integer, Integer> mapCasePositionsPourDe = new HashMap<Integer, Integer>();
		
		for(int i = 1; i< 3; i++){
			mapCasePositionsPourDe.put(i, positionDansTableau++);
		}
		
		for(int i = 3; i < 19; i++){
			mapCasePositionsPourDe.put(i, positionDansTableau++);
			positionDansTableau++;
		}
		for(int i = 19; i <= 20; i++){
			mapCasePositionsPourDe.put(i, positionDansTableau++);
		}
		
		return mapCasePositionsPourDe;
	}
	
	

	public boolean deplacerJeton(int numero, Jeton jeton){
		Util.checkPositive(numero);
		Util.checkObject(jeton);
		
		int position = MapCasePositionsPourDeInit().get(partie.getDe());
		List<Integer> listeCasesAutorisees = isAutorised(position);
		
		
		if(listeCasesAutorisees.contains(numero)){
			for(int i = 0; i < lesCases.size();i++){
				if(lesCases.get(i).getJeton().getValeur() == jeton.getValeur()){
					if(lesCases.get(i).getJeton().equals(jeton)){
						lesCases.get(i).setNumero(numero);
						return true;
					}
				}
			}
			//sinon on rajoute la case
			Case c = new Case(numero);
			c.setJeton(jeton);
			lesCases.add(c);
		}
		return false;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Plateau other = (Plateau) obj;
		if (id != other.id)
			return false;
		return true;
	}

	public int calculerScore() {
		int meilleurScore = 0;
		int score = 1;
		Collections.sort(lesCases, new Comparator<Case>() {
			@Override
			public int compare(Case gauche, Case droite) {
				return gauche.getNumero() > droite.getNumero() ? +1 : gauche.getNumero() < droite
						.getNumero() ? -1 : 0; 
			}
		});
		
		for (int i = 0; i < this.lesCases.size()-1; i++) {
			if (lesCases.get(i).getJeton().getValeur() < lesCases.get(i+1)
					.getJeton().getValeur()) {
				score++;
			} else {
				if (meilleurScore < score)
					meilleurScore = score;
				score = 1;
			}
		}
		if (meilleurScore < score)
			meilleurScore = score;
		return meilleurScore;
	}
	
	public boolean estGagnant(){
		if(this.calculerScore() == 12)
			return true;
		return false;
	}
	
	private boolean siContientCase(int numero){
		for(int i = 0; i < lesCases.size(); i++){
			if( lesCases.get(i).getNumero() == numero)
				return true;
		}
		return false;
	}
	
	private int rechercherADroiteUneCaseLibre(int numero){
		for(int i = numero+1; i<36; i++){
			if(!siContientCase(i)){
				return i;
			}
		}
		return -1;//dans le cas où on trouve pas 
	}
	
	private int rechercherAGaucheUneCaseLibre(int numero) {
		for(int i = numero-1; i >= 0 ; i--){
			if(!siContientCase(i)){
				return i;
			}
		}
		return -1;
	}
	
	public List<Integer> isAutorised(int positionTrouveGraceALaMap){ 
		List<Integer> listeDesPositionsAutorises = new ArrayList<Integer>();
		if(positionTrouveGraceALaMap == 0){
			if(!siContientCase(positionTrouveGraceALaMap)){
				listeDesPositionsAutorises.add(positionTrouveGraceALaMap);
				return listeDesPositionsAutorises;
			}
		}
		if(positionTrouveGraceALaMap == 1){
			if(!siContientCase(positionTrouveGraceALaMap)){
				listeDesPositionsAutorises.add(positionTrouveGraceALaMap);
				return listeDesPositionsAutorises;
			}
			// sinon on vérifie la case juste avant :
			if(!siContientCase(0)){
				listeDesPositionsAutorises.add(0);
			}
			// on vérifie la case juste après :
			if(!siContientCase(2)){
				listeDesPositionsAutorises.add(2);
			}
			//si la liste est pas vide, on peut alors envoyer cette liste
			if(!listeDesPositionsAutorises.isEmpty())
				return listeDesPositionsAutorises;
			//sinon on va aller voir les cases qui suit à droite (vu qu'on est en 1)
			int caseDroiteLibre = rechercherADroiteUneCaseLibre(positionTrouveGraceALaMap);
			if(caseDroiteLibre !=-1)
				listeDesPositionsAutorises.add(caseDroiteLibre);
			return listeDesPositionsAutorises;
		}
		
		if(positionTrouveGraceALaMap == 35){
			if(!siContientCase(positionTrouveGraceALaMap)){
				listeDesPositionsAutorises.add(positionTrouveGraceALaMap);
				return listeDesPositionsAutorises;
			}
		}
		
		if(positionTrouveGraceALaMap == 34){
			if(!siContientCase(positionTrouveGraceALaMap)){
				listeDesPositionsAutorises.add(positionTrouveGraceALaMap);
				return listeDesPositionsAutorises;
			}
			
			if(!siContientCase(33)){
				listeDesPositionsAutorises.add(33);
			}
			
			if(!siContientCase(35)){
				listeDesPositionsAutorises.add(35);
			}
			
			if(!listeDesPositionsAutorises.isEmpty())
				return listeDesPositionsAutorises;
			
			//sinon on va aller voir les cases qui précède à gauche 
			int caseDrouteLibre = rechercherAGaucheUneCaseLibre(positionTrouveGraceALaMap);
			if(caseDrouteLibre !=-1)
				listeDesPositionsAutorises.add(caseDrouteLibre);
			return listeDesPositionsAutorises;
		}
		// on va vérifier si un des deux cases du nombre est vide
		// si c'est le cas alors on rajoute à cet endroit.
		
		if(!siContientCase(positionTrouveGraceALaMap)){
			listeDesPositionsAutorises.add(positionTrouveGraceALaMap);
		}
		if(!siContientCase(positionTrouveGraceALaMap+1)){
			listeDesPositionsAutorises.add(positionTrouveGraceALaMap+1);
		}
		if(!listeDesPositionsAutorises.isEmpty())
			return listeDesPositionsAutorises;
		//si on arrive ici c'est que les deux cases de ce numero sont occupés
		// on va donc rechercher la premiere case libre à gauche puis 
		// la premiere case vide à droite : 
		int premiereCaseLibreAGauche = rechercherAGaucheUneCaseLibre(positionTrouveGraceALaMap);
		int premiereCaseLibreADroite = rechercherADroiteUneCaseLibre(positionTrouveGraceALaMap);
		if(premiereCaseLibreAGauche == -1)//ca veut dire qu'il n'a rien trouve a gauche
			listeDesPositionsAutorises.add(premiereCaseLibreADroite);
		if(premiereCaseLibreADroite == -1)
			listeDesPositionsAutorises.add(premiereCaseLibreAGauche);
		if(!listeDesPositionsAutorises.isEmpty())
			return listeDesPositionsAutorises;
		
		if(premiereCaseLibreADroite - (positionTrouveGraceALaMap +1) == positionTrouveGraceALaMap - premiereCaseLibreAGauche){
			listeDesPositionsAutorises.add(premiereCaseLibreADroite);
			listeDesPositionsAutorises.add(premiereCaseLibreAGauche);
		}// on rajoute les deux car même longueurs
		
		if(premiereCaseLibreADroite-(positionTrouveGraceALaMap+1) <  positionTrouveGraceALaMap-premiereCaseLibreAGauche){
			listeDesPositionsAutorises.add(premiereCaseLibreADroite);
			return listeDesPositionsAutorises;
		}
		else{
			listeDesPositionsAutorises.add(premiereCaseLibreAGauche);
			return listeDesPositionsAutorises;
		}
	}


	

}
